/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#ifndef GIMI_MULTIBUFFER_H
#define GIMI_MULTIBUFFER_H

#include "gimiutils.h"

#include "gimimessagebuffer.h"
#include "multidata.h"
#include "connector.h"

namespace gimi {
  
typedef std::map< unsigned int, GIMIMessageBuffer > t_multiBufferMap;
typedef t_multiBufferMap::iterator t_multiBufferMapIterator;
typedef t_multiBufferMap::const_iterator t_multiBufferMapConstIterator;
typedef std::pair< unsigned int, GIMIMessageBuffer > t_multiBufferMapPair;

/**
 * \class MultiBuffer
 * New class that wraps around GIMI's normal messagebuffer and enables
 * multiplying of received data. Basically GIMI now has multiple
 * messagebuffers, each with its own id. Messagebuffer with GIMI_DEFAULTBUFFERID
 * is created always and is used as a default queue if multiplicity methods
 * have not been used.
 * 
 */
class MultiBuffer {
  public:
    MultiBuffer(const int defQueueSize,
                ActiveStatus* const activeStatus, // Address to instance is const.
                Connector *connPtr); // Connector is provided so MultiData can query names if needed.
    ~MultiBuffer();
    
    /**
     * Inserts the given message to buffer. Specific buffer or multipliers
     * are determined by MultiBuffer.
     * If queue does not exist, it will be created.
     * \warning The pointer is inserted onto buffer, content not copied!
     *
     * \param[in] msg Message itself.
     * \param[in] majorType Major-type of message.
     * \param[in] minorType Minor-type of message.
     * \return GIMIMSG_OK always
     */
    int pushMessage(GIMIMessage *msg,
                    const int majorType,
                    const int minorType);

    /**
     * Returns the first message in defined buffer and removes it from
     * the buffer. If types are not defined, returns first found message.
     * 
     * \param[out] msg Pointer in which pointer to message is written.
     * \param[in] majorType Major-type of message.
     * \param[in] minorType Minor-type of message.
     * \param[in] timeout Time to wait in milliseconds if message isn't found
     * immediately.
     * \param[in] bufferId Bufferid of wanted messagebuffer.
     * \return GIMIMSG_OK If message was popped successfully, GIMIMSG_NOMSGS
     * if type was not found or queue(s) was empty.
     */
    int popMessage(GIMIMessage **msg,
                    const int majorType,
                    const int minorType,
                    const int timeout,
                    const unsigned int bufferId = GIMI_DEFAULTBUFFERID);

    /**
     * Same as above but goes through a range of values.
     */
    int popMessageRange(GIMIMessage **msg,
                        const int majorTypeMin,
                        const int majorTypeMax,
                        const int minorTypeMin,
                        const int minorTypeMax,
                        const int timeout,
                        const unsigned int bufferId = GIMI_DEFAULTBUFFERID);
  
    /**
     * Returns maximum queue size for defined major+minor.
     *
     * \param[in] majorType Majortype of target queue.
     * \param[in] minorType Minortype of target queue.
     * \param[in] bufferId Bufferid of wanted messagebuffer.
     * \return 0 if type not found, else maxsize of given queue.
     */
    int getQueueMaxSize(const int majorType,
                        const int minorType,
                        const unsigned int bufferId = GIMI_DEFAULTBUFFERID) const;


    /**
     * Returns current queue size for defined major+minor.
     * \warning If buffer is set to ignore major/minor types, given arguments
     * are ignored and given value is from the used major or generic buffer.
     *
     * \param[in] majorType Majortype of target queue.
     * \param[in] minorType Minortype of target queue.
     * \param[in] bufferId Bufferid of wanted messagebuffer.
     * \return 0 if type not found, else size of given queue.
     */
    int getQueueCurSize(const int majorType,
                        const int minorType,
                        const unsigned int bufferId = GIMI_DEFAULTBUFFERID) const;

    /**
     * Returns total number of messages in all major/minor queues.
     * \param[in] bufferId Bufferid of wanted messagebuffer.
     */
    int getTotalSize(const unsigned int bufferId = GIMI_DEFAULTBUFFERID) const;

    /**
     * Sets maximum queue size for defined major+minor.
     * Size must be > 0.
     *
     * \param[in] majorType Majortype of target queue.
     * \param[in] minorType Minortype of target queue.
     * \param[in] mSize New maximum size for queue.
     * \param[in] bufferId Bufferid of wanted messagebuffer.
     * \return Boolean for success.
     */
    bool setQueueMaxSize(const int majorType,
                         const int minorType,
                         const int mSize,
                         const unsigned int bufferId = GIMI_DEFAULTBUFFERID);

    /**
     * Clears all messages from selected buffer.
     * 
     * \param[in] bufferId Bufferid of wanted messagebuffer.
     * \return False if bufferId was not found.
     */
    bool clearBuffer(const unsigned int bufferId = GIMI_DEFAULTBUFFERID);
    
    /**
     * Reserves a buffer-identifier for use.
     * 
     * \return Buffer number to use, or 0 if we ran out of numbers (very unlikely,
     * but should be checked against.)
     */
    unsigned int reserveBuffer();
    
    /**
     * Release buffer-identifier. Also checks for and removes any redirections to
     * specified buffer.
     * 
     * \return True if buffer was found and removed, false if queue did not exist.
     */
    bool releaseBuffer(const unsigned int queueIdentifier);
    
    /**
     * \warning If senderName is given, senderId should be up to date also!
     * \return True if adding succeeded, false if given combination already existed.
     */
    bool addRedirection(const unsigned int queueIdentifier,
                        const int majorTypeId,
                        const int minorTypeId = -1,
                        const std::string &senderName = "",
                        const int senderId = -1);
    
    /**
     * 
     * \return True if removing succeeded, false if given combination did not exist.
     */
    bool removeRedirection(const unsigned int queueIdentifier,
                           const int majorTypeId,
                           const int minorTypeId = -1,
                           const int senderId = -1);
    /**
     *
     * \return Number of found redirections.
     */
    int listRedirections(t_redirectionList &redirections,
                         const bool exactTypes = false,
                         const int majorTypeId = -1,
                         const int minorTypeId = -1,
                         const int senderId = -1);
    
    /**
     * Goes through every name in redirections and refreshes it's senderId
     * information
     *
     * \return Number of clientnames that could not be updated.
     */
    int renewRedirectionSenderIds();
    
    /**
     * Calls broadcast on condHandle.
     */
    void broadcastCond();
    
  private:
    // Prevent use of copy constructors.
    MultiBuffer(const MultiBuffer& mb);
    const MultiBuffer& operator=(const MultiBuffer& right);
    
    int defaultQueueSize;
    ActiveStatus* const activeStatus; ///< Pointer to activeStatus of GIMI. The address is const, not content.
    ownMutexHandle bufferMutex; ///< Protects messagebuffers.
    //TODO Implement reader-writer lock once ownutils has support.
    ownCondHandle condHandle; ///< Conditionhandle for waking up receivers
    
    t_multiBufferMap messageBuffers;

  //public:
    // Public so GIMI can use this directly from top-level. Saves from writing
    // interface-wrappers for everything.
    MultiData multiInfo;
};

} // end namespace gimi

#endif // GIMI_MULTIBUFFER_H
